package net.sf.calibur.transport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

class Chunk {
	private byte[] data;
	private Chunk next;
	
	public Chunk(int size) {
		data = new byte[size];
		next = null;
	}
	
	public Chunk getNext() {
		return next;
	}
	
	public void setNext(Chunk next) {
		this.next = next;
	}
	
	public int getSize() {
		return data.length;
	}
	
	public void setValue(int aByte, int offset) {
		data[offset] = (byte) aByte;
	}
	
	public int getValue(int offset) {
		return data[offset];
	}
}

class Pointer {
	private Chunk chunk;
	private int offset;
	
	public Pointer(Chunk chunk) {
		this.chunk = chunk;
		this.offset = 0;
	}
	
	public int getOffset() {
		return offset;
	}
	
	public Chunk getChunk() {
		return chunk;
	}
	
	public void moveTo(Chunk chunk) {
		this.chunk = chunk;
		this.offset = 0;
	}
	
	public void moveToNext() {
		this.chunk = this.chunk.getNext();
		this.offset = 0;
	}
	
	public boolean isUseUp() {
		return offset == chunk.getSize();
	}
	
	public void setValue(int aByte) {
		chunk.setValue(aByte, offset++);
	}
	
	public int getValue() {
		return chunk.getValue(offset++);
	}
}

public class CycleBuffer extends OutputStream {
	public final static int DEFAULT_CHUNK_SIZE = 8;
	public final static int EOF = -1;
	
	private int chunkSize;
	private Chunk head;
	private Pointer readPtr;
	private Pointer writePtr;
	private int chunksNum;
	
	public CycleBuffer() {
		this.chunkSize = DEFAULT_CHUNK_SIZE;
		this.head = new Chunk(chunkSize);
		this.head.setNext(head);
		this.chunksNum = 1;
		
		this.readPtr = new Pointer(head);
		this.writePtr = new Pointer(head);
	}
	
	public int getChunksNum() {
		return chunksNum;
	}

	private void moveToNextChunk() {
		Chunk curr = writePtr.getChunk();
		Chunk next = curr.getNext();
		if (next != readPtr.getChunk()) {
			writePtr.moveTo(next);
		} else {
			Chunk newChunk = new Chunk(chunkSize);
			newChunk.setNext(next);
			curr.setNext(newChunk);
			writePtr.moveTo(newChunk);
			++chunksNum;
		}
		
	}
	
	@Override
	public void write(int aByte) throws IOException {
		if (writePtr.isUseUp()) {
			moveToNextChunk();
		}

		writePtr.setValue(aByte);
	}
	
	public int remaining() {
		if (readPtr.getChunk() == writePtr.getChunk()) {
			return writePtr.getOffset() - readPtr.getOffset();
		} else {
			int total = chunkSize - readPtr.getOffset();
			Chunk curr = readPtr.getChunk().getNext();
			while (true) {
				if (curr == writePtr.getChunk()) {
					total += writePtr.getOffset();
					return total;
				} else {
					total += chunkSize;
				}
				
				curr = curr.getNext();
			}
		}
	}
	
	public InputStream asInputStream() {
		return new InputStream() {
			@Override
			public int read() throws IOException {
				if (remaining() == 0) {
					return EOF;
				}
				
				if (readPtr.isUseUp()) {
					readPtr.moveToNext();
				}
				
				return readPtr.getValue();
			}
			
			@Override
			public int available() {
				return remaining();
			}
		};
	}
	
	public static void main(String[] args) throws Exception {
		CycleBuffer cb = new CycleBuffer();
		InputStream is = cb.asInputStream();
		
		byte[] bytes = new byte[10];
		for (int i = 0; i < 10; ++i) {
			bytes[i] = (byte) i;
		}
		
		assert 1 == 2 : "错误";
	}
}
